package atack.tesis.kelluntekun.ui.componentes;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import org.vaadin.aceeditor.AceEditor;
import org.vaadin.aceeditor.gwt.ace.AceMode;
import org.vaadin.aceeditor.gwt.ace.AceTheme;

import atack.tesis.kelluntekun.domain.Etiqueta;
import atack.tesis.kelluntekun.domain.Snippet;
import atack.tesis.kelluntekun.enums.OpcSnippets;

import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.Reindeer;

/**
 * @author marza
 *
 */
public class codeInfoView extends VerticalLayout{
	private static final long serialVersionUID = 1L;
	
	private HorizontalLayout hlyEditButtons;
	private HorizontalLayout hlyTagsAndLenguaje;
	private VerticalLayout vlyTituloAndDescripcion;
	
	private TabSheet tsEditor;
	private VerticalLayout vlyEditor;
	
	private AceEditor aEditor;
	
	private TextField tfTitulo;
	private TextArea taDescripcion;
	private TextField tfEtiquetas;
	private NativeSelect nsLenguaje;
	private Label lbTags;
	
	private Button btEdit;
	private Button btEliminar;
	private Snippet snippet;
	
	/**
	 * Construye la vista de toda la información contenida en un Snippet.
	 * 
	 */
	public codeInfoView(){
		setMargin(true, false, true, false);
		setSpacing(true);
		setWidth("100%");
		setHeight(null);
		
		buildTituloAndDescripcion();
		addComponent(vlyTituloAndDescripcion);
		setComponentAlignment(vlyTituloAndDescripcion, Alignment.TOP_CENTER);
		
		buildAceEditor();
		addComponent(tsEditor);
		
		buildTagsAndLenguaje();
		addComponent(hlyTagsAndLenguaje);
		
		lbTags=new Label();
		lbTags.setWidth(null);
		addComponent(lbTags);
		
		buildButtons();
		addComponent(hlyEditButtons);
		setComponentAlignment(hlyEditButtons, Alignment.BOTTOM_RIGHT);
		
		addListeners();
		tfTitulo.setTabIndex(1);
		taDescripcion.setTabIndex(2);
		aEditor.setTabIndex(3);
	}
	
	/**
	 * Construye los campos de texto donde se ingrea el titulo y la descripción de un Snippet.
	 */
	private void buildTituloAndDescripcion(){
		vlyTituloAndDescripcion = new VerticalLayout();
		vlyTituloAndDescripcion.setSpacing(true);
		vlyTituloAndDescripcion.setMargin(false, true, false, true);
		vlyTituloAndDescripcion.setWidth("100%");
		vlyTituloAndDescripcion.setHeight(null);
		
		
		tfTitulo=new TextField("Título: ");
		tfTitulo.setImmediate(true);
		tfTitulo.setNullRepresentation("");
		tfTitulo.setWidth("100%");
		tfTitulo.setReadOnly(true);
		tfTitulo.setRequired(true);
		vlyTituloAndDescripcion.addComponent(tfTitulo);
		
		taDescripcion=new TextArea("Descripción: ");
		taDescripcion.setImmediate(true);
		taDescripcion.setNullRepresentation("");
		taDescripcion.setRows(2);
		taDescripcion.setWidth("100%");
		taDescripcion.setReadOnly(true);
		vlyTituloAndDescripcion.addComponent(taDescripcion);
	}
	
	/**
	 * Construye el editor de texto con resalte de codigo dentro de un TabSheet.
	 */
	private void buildAceEditor(){
		
		tsEditor = new TabSheet();
		tsEditor.setStyleName(Reindeer.TABSHEET_SMALL);
		tsEditor.setWidth("100%");
		tsEditor.setHeight("420px");
		
		vlyEditor = new VerticalLayout();
		vlyEditor.setMargin(false);
		vlyEditor.setSpacing(false);
		vlyEditor.setSizeFull();
		
		aEditor = new AceEditor();
		aEditor.setImmediate(false);
		aEditor.setNullRepresentation("");
		aEditor.setWidth("100%");
		aEditor.setHeight("400px");
		aEditor.setRequired(true);
		aEditor.setTheme(AceTheme.crimson_editor);//Chrome
		aEditor.setReadOnly(true);
		
		vlyEditor.addComponent(aEditor);
		tsEditor.addTab(vlyEditor, "Editor");
	}
	
	/**
	 * Construye "la linea"(HorizontalLayout) donde se ingresan las etiquetas y el lenguaje del snippet.
	 */
	private void buildTagsAndLenguaje(){
		hlyTagsAndLenguaje = new HorizontalLayout();
		hlyTagsAndLenguaje.setSpacing(true);
		hlyTagsAndLenguaje.setMargin(false, true, false, false);
		hlyTagsAndLenguaje.setWidth("100%");
		
		tfEtiquetas=new TextField("Etiquetas: ");
		tfEtiquetas.setImmediate(true);
		tfEtiquetas.setNullRepresentation("");
		tfEtiquetas.setWidth("95px");
		tfEtiquetas.setReadOnly(true);
		hlyTagsAndLenguaje.addComponent(tfEtiquetas);
		hlyTagsAndLenguaje.setComponentAlignment(tfEtiquetas, Alignment.MIDDLE_LEFT);
		
		nsLenguaje = new NativeSelect("Lenguaje: ");
		for(AceMode mode:AceMode.values()){
			nsLenguaje.addItem(mode);
		}
		nsLenguaje.setNullSelectionAllowed(false);
		nsLenguaje.setImmediate(true);
		nsLenguaje.setRequired(true);
		nsLenguaje.setEnabled(false);
		hlyTagsAndLenguaje.addComponent(nsLenguaje);
		hlyTagsAndLenguaje.setComponentAlignment(nsLenguaje, Alignment.MIDDLE_RIGHT);
	}
	
	/**
	 * Construye los Botones destinados a guardar los Snippets, editar y cancelar.
	 */
	private void buildButtons(){
		hlyEditButtons= new HorizontalLayout();
		hlyEditButtons.setMargin(false);
		hlyEditButtons.setSpacing(true);
		
		btEliminar= new Button("Eliminar");
		btEliminar.setWidth("65px");
		btEliminar.setStyleName(Reindeer.BUTTON_SMALL);
		hlyEditButtons.addComponent(btEliminar);
		hlyEditButtons.setComponentAlignment(btEliminar, Alignment.BOTTOM_RIGHT);
		
		btEdit= new Button(OpcSnippets.EDITAR.toString());
		btEdit.setWidth("110px");
		btEdit.setStyleName(Reindeer.BUTTON_SMALL);
		hlyEditButtons.addComponent(btEdit);
		hlyEditButtons.setComponentAlignment(btEdit, Alignment.BOTTOM_RIGHT);
	}
	
	/**
	 * Agrega los Listeners para ingresar Etiquetas y
	 * para que el editor de codigos (AceEditor) responda al cambio de lenguaje 
	 * y resalte correctamente el codigo.
	 */
	private void addListeners(){
		
		tfEtiquetas.addListener(new  Property.ValueChangeListener(){
			private static final long serialVersionUID = 1L;

			public void valueChange(ValueChangeEvent event){
				
				if(lbTags.getCaption()!=null&&tfEtiquetas.getValue()!=null){
					//TODO verificar que el tag nuevo no haya sido introducido ya.
					//cambiar lbtags por options groups para que sean "borrables."
					lbTags.setCaption(lbTags.getCaption()+", "+tfEtiquetas.getValue().toString());
				}
				else if (tfEtiquetas.getValue()!=null){
					lbTags.setCaption(tfEtiquetas.getValue().toString());
				}
				tfEtiquetas.setValue(null);
				
			}
		});
		
		nsLenguaje.addListener(new ValueChangeListener(){
			private static final long serialVersionUID = 1L;

			@Override
			public void valueChange(ValueChangeEvent event) {
				aEditor.setMode((AceMode)nsLenguaje.getValue());
			}
		});
	}

	public Button getBtEdit() {
		return btEdit;
	}

	public Button getBtEliminar() {
		return btEliminar;
	}

	/**
	 * coloca los componentes en modo edición.
	 */
	public void setEditMode(){
		
		this.aEditor.setReadOnly(false);
		this.tfTitulo.setReadOnly(false);
		this.taDescripcion.setReadOnly(false);
		this.tfEtiquetas.setReadOnly(false);
		this.nsLenguaje.setEnabled(true);
	}
	
	/**
	 * coloca los componentes en modo solo lectura.
	 */
	public void setReadOnlyMode(){
		this.aEditor.setReadOnly(true);
		this.tfTitulo.setReadOnly(true);
		this.taDescripcion.setReadOnly(true);
		this.tfEtiquetas.setReadOnly(true);
		this.nsLenguaje.setEnabled(false);
	}
	
	/**
	 * Muestra en los campos del componente los datos asociados al Snippet que recibe.
	 * @param s snippet que será mostrado
	 */
	public void setSnippet(Snippet s){
		this.snippet=s;
		
		setEditMode();
		this.tfTitulo.setValue(s.getTitulo());
		this.taDescripcion.setValue(s.getDescripcion());
		this.aEditor.setValue(s.getCodigo());
		this.tfEtiquetas.setValue(getCaptionForTags(s.getEtiquetas()));
		this.nsLenguaje.select(s.getLenguaje());
		aEditor.setMode(s.getLenguaje());
		setReadOnlyMode();
	}
	
	/**
	 * Setea los 'captions' de los botones de la vista.
	 * @param btEditCaption
	 * @param btEliminarCaption
	 */
	public void setButtonsCaptions(String btEditCaption,String btEliminarCaption){
		setBtEditCaption(btEditCaption);
		SetBtEliminarCaption(btEliminarCaption);		
	}
	
	/**
	 * Setea el 'caption' del botón destinado a editar y guardar.
	 * @param btEditCaption
	 */
	public void setBtEditCaption(String btEditCaption){
		this.btEdit.setCaption(btEditCaption);
	}
	
	
	/**
	 * Setea el 'caption' del botón destinado a cancelar y eliminar.
	 * @param btEliminarCaption
	 */
	public void SetBtEliminarCaption(String btEliminarCaption){
		this.btEliminar.setCaption(btEliminarCaption);
	}
	
	
	/**
	 * Retorna el nuevo snippet que será agregado.
	 * @return Snippet
	 */
	public Snippet getNewSnippet(){
	
		Snippet s = new Snippet();
		s.setTitulo(tfTitulo.getValue().toString());
		s.setCodigo(aEditor.getValue().toString());
		s.setDescripcion(taDescripcion.getValue().toString());
		s.setLenguaje((AceMode) nsLenguaje.getValue());
		if(lbTags.getCaption()!=null&&!lbTags.getCaption().equals("")){
			s.setEtiquetas(getEtiquetas());
		}
		
		return s;
	}
	
	/**
	 * Retorna un Snippet con los valores Actulizados cuando se edita un Snippet.
	 * @return Snippet
	 */
	public Snippet getSnippetEditado(){
		Snippet s = this.snippet;
		s.setTitulo(tfTitulo.getValue().toString());
		s.setCodigo(aEditor.getValue().toString());
		s.setDescripcion(taDescripcion.getValue().toString());
		s.setLenguaje((AceMode) nsLenguaje.getValue());
		s.setEtiquetas(getEtiquetas());
		return s;
	}

	/**
	 * Retorna el snippet cargado en la vista.
	 * @return the snippet
	 */
	public Snippet getSnippet() {
		return snippet;
	}
	
	/**
	 * revisa si los campos ingresados como información de un snippet son validos.
	 * @return true si los campos son validos. false de lo contrario.
	 */
	public Boolean isValid(){
		return aEditor.isValid()&&tfTitulo.isValid()&&nsLenguaje.isValid();
	}
	
	/**
	 * Retorna el conjunto de etiquetas candidatas que serán agregadas al nuevo snippet.
	 * @return Set etiquetasCandidatas
	 */
	private Set<Etiqueta> getEtiquetas(){
		Set<Etiqueta> etiquetasCandidatas = new HashSet<Etiqueta>();
		
		StringTokenizer tags = new StringTokenizer(lbTags.getCaption(), ",");
		
		
		while(tags.hasMoreTokens()){
			etiquetasCandidatas.add(new Etiqueta(tags.nextToken().trim()));
		}
		return etiquetasCandidatas;
	}
	
	/**
	 * Recibe el conjunto de etiquetas de un snippets y las "formatea" a una cadena de texto
	 * apta para ser mostrada dentro de un Label.
	 * @param tags
	 * @return String formateado con los Tags.
	 */
	private String getCaptionForTags(Set<Etiqueta> tags){
		String s = " ";
		Iterator<Etiqueta> it = tags.iterator();
		while (it.hasNext()){
			s= s.concat(it.next().getEtiqueta()+" ,");
			if (!it.hasNext()){
				s=s.substring(0, (s.length()-2));
			}
        }
		return s;
		
	}
	
	/**
	 * Remueve el Layout que contiene los botones de edición y eliminación
	 * (consigue un componente de 'solo lectura').
	 */
	public void removeButtons(){
		this.removeComponent(hlyEditButtons);
	}
}
